/*
//简单蜂鸣器
#include<reg52.h>
sbit BUZZ=P1^6;//蜂鸣器控制引脚
unsigned char T0RH=0;//T0高字节
unsigned char T0RL=0;//T0低字节

void OpenBuzz(unsigned int frequ);
void StopBuzz();

void main()
{
    unsigned int i;
    TMOD=0x01;//T0模式一
    EA=1;//全局中断

    while(1)
    {
        OpenBuzz(4000);     //4KHz频率
        for(i=0;i<40000;i++);
        StopBuzz();         //停止
        for(i=0;i<40000;i++);
        OpenBuzz(1000);     //1KHz频率
        for(i=0;i<40000;i++);
        StopBuzz();
        for(i=0;i<40000;i++);
    }
}
//蜂鸣器启动函数
void OpenBuzz(unsigned int frequ)
{
    unsigned int reload;//定时器重载值
    reload=65536-(11059200/12)/(frequ*2);// X*12/11059200=1/(frequ*2)  高、低字节
    T0RH=(unsigned char)(reload>>8);//int16位强制转为两个8位(char)
    T0RL=(unsigned char)reload;
    TH0=0xFF;//设定接近溢出的初值，是定时器马上投入工作
    TL0=0xFE;
    ET0=1;
    TR0=1;
}
//蜂鸣器停止函数
void StopBuzz()
{
    ET0=0;
    TR0=0;
}
//T0中断服务
void InterruptTimer0() interrupt 1
{
    TH0=T0RH;//重新加载重载值
    TL0=T0RL;
    BUZZ=~BUZZ;//反转电平
}
*/


/*
//两只老虎 蜂鸣器
#include <reg52.h>

sbit BUZZ = P1^6;  //蜂鸣器控制引脚

unsigned int code NoteFrequ[] = {  //中音1-7和高音1-7对应频率列表
    523,  587,  659,  698,  784,  880,  988,  //中音1-7
    1047, 1175, 1319, 1397, 1568, 1760, 1976  //高音1-7
};
unsigned int code NoteReload[] = { //中音1-7和高音1-7对应的定时器重载值
    65536 - (11059200/12) / (523*2),  //中音1
    65536 - (11059200/12) / (587*2),  //2
    65536 - (11059200/12) / (659*2),  //3
    65536 - (11059200/12) / (698*2),  //4
    65536 - (11059200/12) / (784*2),  //5
    65536 - (11059200/12) / (880*2),  //6
    65536 - (11059200/12) / (988*2),  //7
    65536 - (11059200/12) / (1047*2), //高音1
    65536 - (11059200/12) / (1175*2), //2
    65536 - (11059200/12) / (1319*2), //3
    65536 - (11059200/12) / (1397*2), //4
    65536 - (11059200/12) / (1568*2), //5
    65536 - (11059200/12) / (1760*2), //6
    65536 - (11059200/12) / (1976*2), //7
};
bit enable = 1;   //蜂鸣器发声使能标志
bit tmrflag = 0;  //定时器中断完成标志
unsigned char T0RH = 0xFF;  //T0重载值的高字节
unsigned char T0RL = 0x00;  //T0重载值的低字节

void PlayTwoTiger();

void main()
{
    unsigned int i;
    
    EA = 1;       //使能全局中断
    TMOD = 0x01;  //配置T0工作在模式1
    TH0 = T0RH;
    TL0 = T0RL;
    ET0 = 1;      //使能T0中断
    TR0 = 1;      //启动T0
    
    while (1)
    {
        PlayTwoTiger();  //播放乐曲--两支老虎
        for (i=0; i<40000; i++);  //停止一段时间
    }
}
//两只老虎乐曲播放函数 
void PlayTwoTiger()
{
    unsigned char beat;   //当前节拍索引
    unsigned char note;   //当前节拍对应的音符
    unsigned int time = 0;      //当前节拍计时
    unsigned int beatTime = 0;  //当前节拍总时间
    unsigned int soundTime = 0; //当前节拍需发声时间
    //两只老虎音符表
    unsigned char code TwoTigerNote[] = {
        1,   2,   3, 1,    1,   2,   3, 1,   3, 4, 5,   3, 4, 5,
        5,6, 5,4, 3, 1,    5,6, 5,4, 3, 1,   1, 5, 1,   1, 5, 1,
    };
    //两只老虎节拍表，4表示一拍，1就是1/4拍，8就是2拍
    unsigned char code TwoTigerBeat[] = {
        4,   4,   4, 4,    4,   4,   4, 4,   4, 4, 8,   4, 4, 8,
        3,1, 3,1, 4, 4,    3,1, 3,1, 4, 4,   4, 4, 8,   4, 4, 8,
    };
    
    for (beat=0; beat<sizeof(TwoTigerNote); )  //用节拍索引作为循环变量
    {
        while (!tmrflag);  //每次定时器中断完成后，检测并处理节拍
        tmrflag = 0;
        if (time == 0)  //当前节拍播完则启动一个新节拍
        {
            note = TwoTigerNote[beat] - 1;
            T0RH = NoteReload[note] >> 8;
            T0RL = NoteReload[note];
            //计算节拍总时间，右移2位相当于除4，移位代替除法可以加快执行速度
            beatTime = (TwoTigerBeat[beat] * NoteFrequ[note]) >> 2;
            //计算发声时间，为总时间的0.75，移位原理同上
            soundTime = beatTime - (beatTime >> 2);
            enable = 1;  //指示蜂鸣器开始发声
            time++;
        }
        else  //当前节拍未播完则处理当前节拍
        {
            if (time >= beatTime)  //当前持续时间到达节拍总时间时归零，
            {                      //并递增节拍索引，以准备启动新节拍
                time = 0;
                beat++;
            }
            else  //当前持续时间未达到总时间时，
            {
                time++;   //累加时间计数
                if (time == soundTime)  //到达发声时间后，指示关闭蜂鸣器，
                {                       //插入0.25*总时间的静音间隔，
                    enable = 0;         //用以区分连续的两个节拍
                }
            }
        }
    }
}
// T0中断服务函数，用于控制蜂鸣器发声 
void InterruptTimer0() interrupt 1
{
    TH0 = T0RH;   //重新加载重载值
    TL0 = T0RL;
    tmrflag = 1;
    if (enable)   //使能时反转蜂鸣器控制电平
        BUZZ = ~BUZZ;
    else          //未使能时关闭蜂鸣器
        BUZZ = 1;
}
*/




/*
//硬件UART查询方式实现串口通信
#include <reg52.h>

void ConfigUART(unsigned int baud);

void main()
{
    //占用了主循环
    ConfigUART(9600);  //配置波特率为9600
    
    while (1)
    {
        while (!RI);     //等待接收完成    接收中断时
        RI = 0;          //清零接收中断标志位
        SBUF = SBUF + 1; //接收到的数据+1后，发送回去    1发送BUF  2接收BUF
        while (!TI);     //等待发送完成    发送中断时
        TI = 0;          //清零发送中断标志位
    }
}
// 串口配置函数，baud-通信波特率 
void ConfigUART(unsigned int baud)
{
    SCON  = 0x50;  //配置串口为模式1   0101 0000
    TMOD &= 0x0F;  //清零T1的控制位
    TMOD |= 0x20;  //配置T1为模式2
    TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值
    TL1 = TH1;     //初值等于重载值
    ET1 = 0;       //禁止T1中断 T1只做波特率发生器
    TR1 = 1;       //启动T1
}
*/



/*
//UART中断方式实现串口通信   不占用主循环
#include <reg52.h>

void ConfigUART(unsigned int baud);

void main()
{
    EA = 1;   //使能总中断
    ConfigUART(9600);  //配置波特率为9600
    while (1);
}
//串口配置函数，baud-通信波特率 
void ConfigUART(unsigned int baud)
{
    SCON  = 0x50;  //配置串口为模式1
    TMOD &= 0x0F;  //清零T1的控制位
    TMOD |= 0x20;  //配置T1为模式2
    TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值
    TL1 = TH1;     //初值等于重载值
    ET1 = 0;       //禁止T1中断
    ES  = 1;       //使能串口中断
    TR1 = 1;       //启动T1
}
// UART中断服务函数 
void InterruptUART() interrupt 4    //4*8+3=35   0x23
{
    if (RI)  //接收到字节
    {
        RI = 0;   //手动清零接收中断标志位
        SBUF = SBUF + 1;  //接收的数据+1后发回，左边是发送SBUF，右边是接收SBUF
    }
    if (TI)  //字节发送完毕
    {
        TI = 0;   //手动清零发送中断标志位
    }
}
*/






//UART串口通信及控制数码管
#include <reg52.h>

sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

unsigned char code LedChar[] = {  //数码管显示字符转换表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[7] = {  //数码管+独立LED显示缓冲区
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char T0RH = 0;  //T0重载值的高字节
unsigned char T0RL = 0;  //T0重载值的低字节
unsigned char RxdByte = 0;  //串口接收到的字节

void ConfigTimer0(unsigned int ms);
void ConfigUART(unsigned int baud);

void main()
{
    EA = 1;       //使能总中断
    ENLED = 0;    //选择数码管和独立LED
    ADDR3 = 1;
    ConfigTimer0(1);   //配置T0定时1ms
    ConfigUART(9600);  //配置波特率为9600
    
    while (1)
    {   //将接收字节在数码管上以十六进制形式显示出来
        LedBuff[0] = LedChar[RxdByte & 0x0F];
        LedBuff[1] = LedChar[RxdByte >> 4];
    }
}
// 配置并启动T0，ms-T0定时时间 
void ConfigTimer0(unsigned int ms)
{
    unsigned long tmp;  //临时变量
    
    tmp = 11059200 / 12;      //定时器计数频率
    tmp = (tmp * ms) / 1000;  //计算所需的计数值
    tmp = 65536 - tmp;        //计算定时器重载值
    tmp = tmp + 13;           //补偿中断响应延时造成的误差
    T0RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0;   //清零T0的控制位
    TMOD |= 0x01;   //配置T0为模式1
    TH0 = T0RH;     //加载T0重载值
    TL0 = T0RL;
    ET0 = 1;        //使能T0中断
    TR0 = 1;        //启动T0
}
// 串口配置函数，baud-通信波特率 
void ConfigUART(unsigned int baud)
{
    SCON  = 0x50;  //配置串口为模式1
    TMOD &= 0x0F;  //清零T1的控制位
    TMOD |= 0x20;  //配置T1为模式2
    TH1 = 256 - (11059200/12/32)/baud;  //计算T1重载值
    TL1 = TH1;     //初值等于重载值
    ET1 = 0;       //禁止T1中断
    ES  = 1;       //使能串口中断
    TR1 = 1;       //启动T1
}
// LED动态扫描刷新函数，需在定时中断中调用 
void LedScan()
{
    static unsigned char i = 0;  //动态扫描索引
    
    P0 = 0xFF;             //关闭所有段选位，显示消隐
    P1 = (P1 & 0xF8) | i;  //位选索引值赋值到P1口低3位
    P0 = LedBuff[i];       //缓冲区中索引位置的数据送到P0口
    if (i < 6)             //索引递增循环，遍历整个缓冲区
        i++;
    else
        i = 0;
}
// T0中断服务函数，完成LED扫描 
void InterruptTimer0() interrupt 1
{
    TH0 = T0RH;  //重新加载重载值
    TL0 = T0RL;
    LedScan();   //LED扫描显示
}
// UART中断服务函数 
void InterruptUART() interrupt 4
{
    if (RI)  //接收到字节
    {
        RI = 0;  //手动清零接收中断标志位
        RxdByte = SBUF;  //接收到的数据保存到接收字节变量中
        SBUF = RxdByte;  //接收到的数据又直接发回，叫作-"echo"，
                         //用以提示用户输入的信息是否已正确接收
    }
    if (TI)  //字节发送完毕
    {
        TI = 0;  //手动清零发送中断标志位
    }
}
